﻿using System;
using System.Collections.Generic;
using System.Text;


namespace advadev.CanOpen
{

    /// <summary>
    /// Represents a can open unicode string
    /// </summary>
    public class CanOpenUnicodeString : ICanOpenValueObject
    {

        /// <summary>for internal use</summary>
        private string _value = string.Empty;


        /// <summary>
        /// Initializes a new instance of the CanOpenUnicodeString class by the
        /// given parameter
        /// </summary>
        /// <param name="index">
        /// Index of the object
        /// </param>
        /// <param name="subIndex">
        /// Sub index of the object
        /// </param>
        /// <param name="isReadable">
        /// Flag indicating whether the object is readable
        /// </param>
        /// <param name="isWriteable">
        /// Flag indicating whether the object is writeable
        /// </param>
        public CanOpenUnicodeString(UInt16 index, byte subIndex, bool isReadable, bool isWriteable)
        {
            Index = index;
            SubIndex = subIndex;
            IsReadable = isReadable;
            IsWriteable = isWriteable;
            ByteOrder = CanOpenByteOrder.LittleEndian;
            LastServiceDataObjectUploadSequence = new List<CanOpenMessage>();
            LastServiceDataObjectDownloadSequence = new List<CanOpenMessage>();
        }


        /// <summary>
        /// Gets or sets the value
        /// </summary>
        /// <remarks>
        /// Null will be used as empty string
        /// </remarks>
        public string Value
        {
            get
            {
                return _value;
            }
            set
            {
                string newValue = (value == null ? string.Empty : value);
                if (newValue != _value)
                {
                    string oldValue = _value;
                    _value = newValue;
                    onRaiseValueChanged(new ValueChangedEventArgs(oldValue, _value));
                }
            }
        }


        /// <summary>
        /// Raises the value changing event
        /// </summary>
        /// <param name="args">
        /// Event argument
        /// </param>
        private void onRaiseValueChanging(ValueChangingEventArgs args)
        {
            if (_valueChanging != null)
            {
                _valueChanging(this, args);
            }
        }

        /// <summary>
        /// Raises the value changed event
        /// </summary>
        /// <param name="args">
        /// Event argument
        /// </param>
        private void onRaiseValueChanged(ValueChangedEventArgs args)
        {
            if (_valueChanged != null)
            {
                _valueChanged(this, args);
            }
        }


        #region ICanOpenValueObject implementation
        /// <summary>for internal use</summary>
        private event ValueChangingEventHandler _valueChanging;
        /// <summary>for internal use</summary>
        private event ValueChangedEventHandler _valueChanged;
        /// <summary>for internal use</summary>
        private bool _isValueChangedEventRaisingPaused;
        /// <summary>for internal use</summary>
        private string _oldValue;

        /// <summary>
        /// Occures when the value is changing
        /// </summary>
        public event ValueChangingEventHandler ValueChanging
        {
            add
            {
                _valueChanging += value;
            }
            remove
            {
                _valueChanging -= value;
            }
        }

        /// <summary>
        /// Occures when the value is changed
        /// </summary>
        public event ValueChangedEventHandler ValueChanged
        {
            add
            {
                _valueChanged += value;
            }
            remove
            {
                _valueChanged -= value;
            }
        }

        /// <summary>
        /// Gets the index of the object
        /// </summary>
        public UInt16 Index
        {
            get;
            private set;
        }

        /// <summary>
        /// Gets the sub index of the object
        /// </summary>
        public byte SubIndex
        {
            get;
            private set;
        }

        /// <summary>
        /// Gets or sets a flag indicating whether the object is readable
        /// </summary>
        public bool IsReadable
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets a flag indicating whether the object is writeable
        /// </summary>
        public bool IsWriteable
        {
            get;
            set;
        }

        /// <summary>
        /// Gets a flag indicating whether a expedited download transfer is
        /// possible for this object
        /// </summary>
        bool ICanOpenValueObject.IsExpeditedDownloadTransferPossible
        {
            get
            {
                return (Value.Length <= 2);
            }
        }

        /// <summary>
        /// Gets a flag indicating whether a expedited upload transfer is
        /// possible for this object
        /// </summary>
        bool ICanOpenValueObject.IsExpeditedUploadTransferPossible
        {
            get
            {
                return false;
            }
        }

        /// <summary>
        /// Gets a flag indicating whether a block download transfer is advised
        /// for this object
        /// </summary>
        bool ICanOpenValueObject.IsBlockDownloadTransferAdvised
        {
            get
            {
                return (Value.Length > 14);
            }
        }

        /// <summary>
        /// Gets a flag indicating whether a block upload transfer is advised
        /// for this object
        /// </summary>
        bool ICanOpenValueObject.IsBlockUploadTransferAdvised
        {
            get
            {
                return false;
            }
        }

        /// <summary>
        /// Gets or sets a flag indicating whether the value changed event
        /// raising is paused
        /// </summary>
        public bool IsValueChangedEventRaisingPaused
        {
            get
            {
                return _isValueChangedEventRaisingPaused;
            }
            set
            {
                if (value != _isValueChangedEventRaisingPaused)
                {
                    _isValueChangedEventRaisingPaused = value;
                    if (!_isValueChangedEventRaisingPaused)
                    {
                        if (_value != _oldValue)
                        {
                            onRaiseValueChanged(new ValueChangedEventArgs(_oldValue, _value));
                        }
                        _oldValue = _value;
                    }
                    else
                    {
                        _oldValue = _value;
                    }
                }
            }
        }

        /// <summary>
        /// Gets or sets the byte order
        /// </summary>
        public CanOpenByteOrder ByteOrder
        {
            get;
            set;
        }

        /// <summary>
        /// Gets the last service data object upload sequence
        /// </summary>
        public IList<CanOpenMessage> LastServiceDataObjectUploadSequence
        {
            get;
            private set;
        }

        /// <summary>
        /// Gets the last service data object download sequence
        /// </summary>
        public IList<CanOpenMessage> LastServiceDataObjectDownloadSequence
        {
            get;
            private set;
        }


        /// <summary>
        /// Converts the value of the object to a byte array
        /// </summary>
        /// <returns>
        /// Value of the object converted to a byte array
        /// </returns>
        IList<byte> ICanOpenValueObject.ToByteArray()
        {
            IList<byte> result = new List<byte>();
            if (Value == string.Empty)
            {
                result.Add(0x00);
            }
            else
            {
                foreach (byte b in Encoding.Unicode.GetBytes(Value))
                {
                    result.Add(b);
                }
            }
            return result;
        }

        /// <summary>
        /// Converts the given byte array and sets the result as value
        /// </summary>
        /// <param name="byteArray">
        /// Byte array to convert and set as value
        /// </param>
        void ICanOpenValueObject.FromByteArray(IList<byte> byteArray)
        {
            try
            {
                string oldValue = _value;
                _value = string.Empty;
                byte[] bytes = new byte[byteArray.Count];
                byteArray.CopyTo(bytes, 0);
                _value = Encoding.Unicode.GetString(bytes);
                if (_value != oldValue)
                {
                    if (!IsValueChangedEventRaisingPaused)
                    {
                        onRaiseValueChanged(new ValueChangedEventArgs(oldValue, _value));
                    }
                }
            }
            catch { }
        }

        /// <summary>
        /// Converts the given byte array and sets the result as value after
        /// validating the value
        /// </summary>
        /// <param name="byteArray">
        /// Byte array to convert and set as value
        /// </param>
        /// <param name="abortCode">
        /// Abort code if the validation failed (otherwise null)
        /// </param>
        /// <returns>
        /// True, if the new value is valid, false otherwise
        /// </returns>
        bool ICanOpenValueObject.FromByteArray(IList<byte> byteArray, out CanOpenAbortCode abortCode)
        {
            try
            {
                string newValue = string.Empty;
                byte[] bytes = new byte[byteArray.Count];
                byteArray.CopyTo(bytes, 0);
                newValue = Encoding.Unicode.GetString(bytes);
                ValueChangingEventArgs args = new ValueChangingEventArgs(_value, newValue);
                onRaiseValueChanging(args);
                if (args.AbortCode != null)
                {
                    abortCode = args.AbortCode;
                    return false;
                }
                string oldValue = _value;
                _value = newValue;
                if (_value != oldValue)
                {
                    if (!IsValueChangedEventRaisingPaused)
                    {
                        onRaiseValueChanged(new ValueChangedEventArgs(oldValue, _value));
                    }
                }
            }
            catch
            {
                abortCode = new CanOpenAbortCode(0x30, 0x00, 0x09, 0x06);
                return false;
            }
            abortCode = null;
            return true;
        }
        #endregion ICanOpenValueObject implementation

    }

}
